package com.cuiweiyou.bluemesh.bluetooth;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanResult;
import android.content.Context;
import android.util.Log;

import com.cuiweiyou.bluemesh.base.BluetoothApp;
import com.cuiweiyou.bluemesh.model.EventCodedModel;
import com.cuiweiyou.bluemesh.model.EventDeviceModel;
import com.cuiweiyou.bluemesh.model.LightModel;
import com.cuiweiyou.bluemesh.util.NicknameSqlUtil;
import com.cuiweiyou.bluemesh.util.RssiUtil;
import com.cuiweiyou.bluemesh.util.StringUtil;

import org.greenrobot.eventbus.EventBus;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.UUID;

/**
 * Created by macpro on 2018/10/24.
 */

public class BluetoothMaster {
    private static BluetoothMaster instance;

    private BluetoothAdapter bluetoothAdapter;
    private BluetoothLeScanner bluetoothLeScanner;

    /**
     * [组名, 集合[蓝牙设备]]
     */
    private Map<String, List<LightModel>> bluetoothDeviceMap;

    private BluetoothManager bluetoothManager;

    private List<String> meshNameList;
    private List<LightModel> currentDeviceList;
    private Map<String, BluetoothGatt> currentBluetoothGattMap;
    private Map<String, BluetoothGattCharacteristic> currentCharacteristicHandlerMap;

    private String lastCode;

    private BluetoothMaster() {
        bluetoothDeviceMap = new HashMap<>();
        meshNameList = new ArrayList<>();
        currentDeviceList = new ArrayList<>();
        currentBluetoothGattMap = new HashMap<>();
        currentCharacteristicHandlerMap = new HashMap<>();
    }

    public static BluetoothMaster getInstance() {
        if (null == instance) {
            synchronized (BluetoothMaster.class) {
                if (null == instance) {
                    instance = new BluetoothMaster();
                }
            }
        }
        return instance;
    }

    /**
     * 手机是否支持蓝牙
     *
     * @return
     */
    public boolean isDeviceEnable() {
        bluetoothManager = (BluetoothManager) BluetoothApp.getContext().getSystemService(Context.BLUETOOTH_SERVICE);
        bluetoothAdapter = bluetoothManager.getAdapter();

        if (bluetoothAdapter == null) {
            return false;
        }

        return true;
    }

    /**
     * 蓝牙功能是否打开
     *
     * @return
     */
    public boolean isBluetoothEnable() {
        if (!bluetoothAdapter.isEnabled() || BluetoothAdapter.STATE_OFF == bluetoothAdapter.getState()) {
            return false;
        }
        return true;
    }

    /**
     * 加载初始化时添加的设备
     * @param map
     */
    public void initDeviceMap(Map<String, List<LightModel>> map) {
        Iterator<String> iterator = map.keySet().iterator();
        while (iterator.hasNext()) {
            String groupname = iterator.next();
            List<LightModel> lightList = map.get(groupname);
            bluetoothDeviceMap.put(groupname, lightList);
        }
    }

    /**
     * 1.扫描设备
     *
     * @param name 灯网的名称-mesh中蓝牙的Name
     */
    public void scan(String name) {
        if (!meshNameList.contains(name)) {
            meshNameList.add(name);
        }

        List<LightModel> lightModelList = bluetoothDeviceMap.get(name);
        if (null == lightModelList) {
            lightModelList = new ArrayList<>();
            bluetoothDeviceMap.put(name, lightModelList);
        }

        if (null == bluetoothLeScanner) {
            bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner();
            bluetoothLeScanner.startScan(scanCallback);
        }
    }

    /**
     * 2.扫描结果返回
     */
    private ScanCallback scanCallback = new ScanCallback() {

        // 除非手动停止，此方法一直执行。即实时查找周边设备
        @Override
        public void onScanResult(int callbackType, ScanResult result) {
            if (null != result) {
                BluetoothDevice device = result.getDevice();
                String name = device.getName();
                String address = device.getAddress();

                if (!meshNameList.contains(name)) {
                    return;
                }

                LightModel lightModel = new LightModel(); // 扫描到设备，创建临时对象
                lightModel.setAddress(address); // 是否已经保存的条件是对address进行比较

                List<LightModel> lightModelList = bluetoothDeviceMap.get(name);
                if (!lightModelList.contains(lightModel)) {
                    lightModelList.add(lightModel);
                } else {
                    int index = lightModelList.indexOf(lightModel);
                    lightModel = lightModelList.get(index);
                }

                int rssi = result.getRssi();

                lightModel.setDevice(device);
                lightModel.setName(name);
                lightModel.setRssi(rssi);
                lightModel.setMile(RssiUtil.rssiToMile(rssi));
                lightModel.setLastLiveTime(System.currentTimeMillis());
                lightModel.setNickname(NicknameSqlUtil.getInstance().getNickName(address));

                BluetoothRssiSortor.sortDevicesByRssi(bluetoothDeviceMap);
                BluetoothStateTool.flushOfflineState(bluetoothDeviceMap);
                EventBus.getDefault().postSticky(new EventDeviceModel()); // 扫描有结果了，刷新ui
            }
        }

        @Override
        public void onBatchScanResults(List<ScanResult> results) {
            Log.e("ard", "onBatchScanResults：" + results.size());
        }

        @Override
        public void onScanFailed(int errorCode) {
            Log.e("ard", "onScanFailed errorCode：" + errorCode);
        }
    };

    /**
     * 3.连接某个设备
     *
     * @param model
     */
    public void connectDevice(LightModel model) {
        if (!currentDeviceList.contains(model)) {
            currentDeviceList.add(model);
        }

        String name = model.getName();
        String address = model.getAddress();

        model.getDevice().connectGatt(BluetoothApp.getContext(), true, new GroupBluetoothGattCallback()); // 连接这台蓝牙设备
    }

    /**
     * 4.断开已经连接的蓝牙
     */
    public void dissConnectDevice(LightModel lightModel) {
        lightModel.setAdmi(false);
        EventBus.getDefault().postSticky(new EventDeviceModel()); // 断开某个设备连接了，刷新ui
    }

    /**
     * 释放
     */
    public void release() {
        bluetoothLeScanner.stopScan(scanCallback);
        currentCharacteristicHandlerMap.clear();

        Iterator<String> gattIterator = currentBluetoothGattMap.keySet().iterator();
        while (gattIterator.hasNext()) {
            String mac = gattIterator.next();
            BluetoothGatt bluetoothGatt = currentBluetoothGattMap.get(mac);
            bluetoothGatt.disconnect();
            bluetoothGatt.close();
        }
        currentBluetoothGattMap.clear();
        currentDeviceList.clear();
    }

    /**
     * 5.获取已连接设备的Service
     */
    public void getGattDeviceServices(LightModel lightModel) {
        String address = lightModel.getAddress();
        BluetoothGatt bluetoothGatt = currentBluetoothGattMap.get(address);
        if (null != bluetoothGatt) {
            bluetoothGatt.discoverServices(); // 扫描开放的服务。回调BluetoothGattCallback gattCallback对象的onServicesDiscovered方法
        }
    }

    /**
     * 连接成功后回调此GATT对象
     */
    private class GroupBluetoothGattCallback extends BluetoothGattCallback {

        /**
         * BluetoothGattCallback中的方法。获取和蓝牙设备的连接状态
         *
         * @param gatt     返回连接建立的gatt对象
         * @param status   返回的是此次gatt操作的结果，成功了返回0
         * @param newState 每次client连接或断开连接状态变化
         */
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            String address = gatt.getDevice().getAddress();
            LightModel lightModel = getOneCurrentBluetooth(address);

            if (status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothGatt.STATE_CONNECTED) { // 0-gatt连接成功
                if (null != lightModel) {
                    lightModel.setAdmi(true);
                    currentBluetoothGattMap.put(address, gatt);
                    //bluetoothLeScanner.stopScan(scanCallback); // 停止扫描
                    getGattDeviceServices(lightModel);
                }
            } else { // 失败或断开。这个状态的回调比较慢
                if (null != lightModel) {
                    dissConnectDevice(lightModel); // 意外断开了连接
                }
            }
        }

        /**
         * BluetoothGattCallback中的方法。获取蓝牙设备开放的服务
         * 当执行 currentBluetoothGatt.discoverServices(); 后才可以获取到蓝牙设备的服务
         *
         * @param gatt   返回的是本次连接的gatt对象，即currentBluetoothGatt
         * @param status
         */
        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            String address = gatt.getDevice().getAddress();
            List<BluetoothGattService> gattServiceList = gatt.getServices();

            // 遍历Service
            for (int i = 0; i < gattServiceList.size(); i++) {
                BluetoothGattService gattService = gattServiceList.get(i);
                String serviceUUID = gattService.getUuid().toString();
                List<BluetoothGattCharacteristic> characteristicList = gattService.getCharacteristics();

                // 遍历Characteristic
                for (int j = 0; j < characteristicList.size(); j++) {
                    BluetoothGattCharacteristic characteristic = characteristicList.get(j);
                    gatt.setCharacteristicNotification(characteristic, true); // 设置可接收回调消息

                    String characteristicUUID = characteristic.getUuid().toString();
                    int properties = characteristic.getProperties();

                    // 这个特征码是手机向蓝牙发数据的。根据蓝牙模块型号不同特征码uuid也可能不同
                    if (characteristicUUID.startsWith("0000ffe2")) {
                        currentCharacteristicHandlerMap.put(address, characteristic);

                        sendBlueToothCode(gatt, characteristic); // 使用刚刚连接成功的设备发指令
                    }
                }
            }
        }

        /**
         * 从蓝牙模块读取
         * Arduino向蓝牙串口写输入后响应
         *
         * @param gatt
         * @param characteristic
         */
        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
            byte[] data = characteristic.getValue();
            String hexString = StringUtil.bytesToHexString(data);
            String address = gatt.getDevice().getAddress();
            String nickName = NicknameSqlUtil.getInstance().getNickName(address);

            disconnectDevices(gatt, characteristic, 0);
        }

        /**
         * 向蓝牙模块写入
         * 手机向蓝牙write操作后回调
         *
         * @param gatt
         * @param characteristic
         * @param status
         */
        @Override
        public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            byte[] value = characteristic.getValue();
            String hexString = StringUtil.bytesToHexString(value);
            String address = gatt.getDevice().getAddress();
            String nickName = NicknameSqlUtil.getInstance().getNickName(address);

            disconnectDevices(gatt, characteristic, status);
        }

        @Override
        public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            super.onCharacteristicRead(gatt, characteristic, status);
            Log.e("ard", "onCharacteristicRead：" + characteristic.hashCode() + "，" + status);
        }

        /**
         * Callback indicating the result of a descriptor write operation.
         *
         * @param gatt
         * @param descriptor
         * @param status
         */
        @Override
        public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
            Log.e("ard", "onDescriptorWrite");
        }

        //
        @Override
        public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
            super.onDescriptorRead(gatt, descriptor, status);
            UUID uuid = descriptor.getUuid();
            byte[] data = descriptor.getValue();
            Log.e("ard", "onDescriptorRead，uuid：" + uuid + "，data：" + Arrays.toString(data));
        }
    }

    private void disconnectDevices(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
        byte[] value = characteristic.getValue();
        String hexString = StringUtil.bytesToHexString(value);
        String address = gatt.getDevice().getAddress();

        gatt.disconnect(); // 当有两组mesh时，不断开 会被scanner隐藏。尚不知何故
        currentCharacteristicHandlerMap.remove(address);

        EventBus.getDefault().post(new EventCodedModel(gatt.getDevice().getName(), status, lastCode, hexString)); // 通知UI发送指令结束
    }

    private LightModel getOneCurrentBluetooth(String address) {
        for (int i = 0; i < currentDeviceList.size(); i++) {
            LightModel lightModel = currentDeviceList.get(i);
            String mac = lightModel.getAddress();
            if (mac.equalsIgnoreCase(address)) {
                return lightModel;
            }
        }

        return null;
    }

    /**
     * 向设备发送指令
     *
     * @param model 设备
     * @param code  指令
     */
    public void processDevice(LightModel model, String code) {
        lastCode = code;
        String address = model.getAddress();
        BluetoothGattCharacteristic currentCharacteristicHandler = currentCharacteristicHandlerMap.get(address);
        BluetoothGatt currentBluetoothGatt = currentBluetoothGattMap.get(address);

        if (null == currentCharacteristicHandler || null == currentBluetoothGatt || !currentDeviceList.contains(model)) { // mesh中仅使用一个已连接设备即可
            connectDevice(model);
        } else {
            sendBlueToothCode(currentBluetoothGatt, currentCharacteristicHandler);// 使用已经连接的设备发送指令
        }
    }

    /**
     * 扫描到的全部蓝牙设备
     *
     * @return
     */
    public Map<String, List<LightModel>> getDeviceMap() {
        return bluetoothDeviceMap;
    }

    /**
     * 扫描到的全部蓝牙设备
     *
     * @return
     */
    public List<LightModel> getDeviceListByGroup(String name) {
        return bluetoothDeviceMap.get(name);
    }

    /**
     * 刷新设备的开关状态
     *
     * @param status 0-关，1-开
     */
    public void flushDevices(String group, int status) {
        List<LightModel> lightModels = bluetoothDeviceMap.get(group);
        for (LightModel light : lightModels) {
            light.setOpen(1 == status);
        }
    }

    /**
     * 向蓝牙模块发消息
     */
    public void sendBlueToothCode(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
        characteristic.setValue(StringUtil.hexStringToByteArray(lastCode.toUpperCase()));
        gatt.writeCharacteristic(characteristic); // 回调BluetoothGattCallback gattCallback的onCharacteristicWrite方法
    }
}
